from PIL import Image
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QApplication
from screeninfo import get_monitors

import basic_operations as b
from ui.CropLabel import CropLabel
from curve_widget import MyCurveWidget
from ui.ImageWindow import Ui_MainWindow
from canvas_window import MyCanvasWindow
import sys

import numpy as np


class MyImageWindow(QMainWindow):
    # define a signal that emits when this window is closed
    closed = pyqtSignal()

    def __init__(self, filename, x, y):
        super().__init__()
        # init data fields
        self.filename = filename
        self.x = x
        self.y = y
        self.ratio = x / y
        self.img = Image.open(filename)  # This is for backup
        self.tmp_img = self.img.copy()  # This is for image processing
        self.tmp_img_copy = None
        self.undo_stack = []
        self.redo_stack = []

        self.curve_widget = None
        self.unsaved = False

        # set up ui
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # get monitor size and resize accordingly
        monitor = get_monitors()[0]
        self.screen_x = monitor.width
        self.screen_y = monitor.height
        self.resize_ui()
        self.init_actions()

        self.target = np.zeros(256)

    def closeEvent(self, event):
        """
        重写closeEvent()函数，在窗口关闭时发送信号
        Emit a signal when window is closed.
        """
        if self.unsaved:
            msg = QMessageBox()
            msg.setWindowTitle('Warning')
            msg.setText('Do you want to save the changes?')
            msg.setIcon(QMessageBox.Warning)
            msg.setStandardButtons(QMessageBox.Save | QMessageBox.Ignore | QMessageBox.Cancel)
            ret = msg.exec_()
            if ret == QMessageBox.Save:
                self.save_file()
            elif ret == QMessageBox.Cancel:
                return
        # noinspection PyUnresolvedReferences
        self.closed.emit()

    def resize_ui(self):
        """
        根据图片大小调整窗口及图像标签大小
        Resize the label and window to fit the image size, and show image.
        """
        if self.y > self.screen_y:
            x = self.screen_y * 0.8 * self.ratio
            y = self.screen_y * 0.8
        elif self.x > self.screen_x:
            x = self.screen_x * 0.8
            y = self.screen_x * 0.8 / self.ratio

        else:
            x = self.x
            y = self.y
        self.ui.imageLabel.setGeometry(QtCore.QRect(0, 0, x, y))
        if sys.platform == 'win32':
            self.setFixedSize(x, y + 25)
        else:
            self.setFixedSize(x, y)
        self.setWindowTitle(self.filename.split('/')[-1])
        self.update_image()

    def init_actions(self):
        """
        连接信号槽，也就是事件发生时要触发的函数
        """
        self.ui.actionSave.triggered.connect(self.save_file)
        self.ui.actionSave_As.triggered.connect(self.save_as)
        self.ui.actionUndo.triggered.connect(self.undo)
        self.ui.actionGrayscale.triggered.connect(self.convert_to_grayscale)
        self.ui.actionCurve.triggered.connect(self.curve)
        self.ui.actionBinary.triggered.connect(self.convert_to_binary)
        self.ui.actionEqualization.triggered.connect(self.hist_equalize)
        self.ui.actionRedo.triggered.connect(self.redo)
        self.ui.actionOpen_On_Canvas.triggered.connect(self.open_canvas)
        self.ui.actionCrop.triggered.connect(self.crop_enter)

    def open_canvas(self):
        # prompt for saving changes
        if self.unsaved:
            msg = QMessageBox()
            msg.setWindowTitle('Warning')
            msg.setText('Do you want to keep the last change before opening on canvas?')
            msg.setIcon(QMessageBox.Warning)
            msg.setStandardButtons(QMessageBox.Save | QMessageBox.Ignore | QMessageBox.Cancel)
            ret = msg.exec_()
            if ret == QMessageBox.Ignore:
                if len(self.undo_stack) > 0:
                        self.undo()
            elif ret == QMessageBox.Cancel:
                return
        canvas_window = MyCanvasWindow('./test/tmp_img', self.filename.split('/')[-1])
        canvas_window.show()
        self.hide()
        canvas_window.closed.connect(lambda x: self.reshow_from_canvas_window(x))

    def reshow_from_canvas_window(self, x):
        self.tmp_img = Image.open('./test/tmp_img')
        self.update_image()
        self.show()
        self.unsaved = x

    def save_file(self):  # 保存文件
        self.tmp_img.save(self.filename, 'PNG')
        self.unsaved = False

    def save_as(self):  # 另存为
        filename, _ = QFileDialog.getSaveFileName(self, 'Save As PNG Image', self.filename, 'PNG Image (*.png)')
        if filename:
            self.tmp_img.save(filename, 'PNG')
            self.unsaved = False
        else:
            msg = QMessageBox()
            msg.setWindowTitle('Error')
            msg.setText('File could not be saved.')
            msg.setIcon(QMessageBox.Critical)
            _ = msg.exec_()

    def undo(self):  # 撤销
        self.redo_stack.append(self.tmp_img)
        self.tmp_img = self.undo_stack.pop()

        # 为了适配裁剪图片，在undo里添加了改变窗口大小的操作
        self.x = self.tmp_img.width
        self.y = self.tmp_img.height
        self.resize_ui()
        # self.update_image()

        self.ui.actionRedo.setEnabled(True)
        if len(self.undo_stack) == 0:
            self.ui.actionUndo.setEnabled(False)

    def redo(self):
        self.undo_stack.append(self.tmp_img)
        self.tmp_img = self.redo_stack.pop()

        # 为了适配裁剪图片，添加了改变窗口大小的操作
        self.x = self.tmp_img.width
        self.y = self.tmp_img.height
        self.resize_ui()
        # self.update_image()

        if len(self.redo_stack) == 0:
            self.ui.actionRedo.setEnabled(False)
        self.ui.actionUndo.setEnabled(True)

    def convert_to_grayscale(self):
        self.write_changes()
        self.tmp_img, _ = b.convert_to_grayscale(self.tmp_img)
        self.update_image()

    def convert_to_binary(self):
        self.write_changes()
        self.tmp_img = b.convert_to_binary(self.tmp_img)
        self.update_image()

    def hist_equalize(self):
        self.write_changes()
        self.tmp_img = b.hist_equalize(self.tmp_img)
        self.update_image()

    def curve(self):
        # copy self.tmp_img
        self.tmp_img_copy = self.tmp_img.copy()

        # 打开曲线控件
        self.curve_widget = MyCurveWidget(self.tmp_img.mode, self.target)
        # 连接信号，当收到update_window_image信号时，运行update_from_curve_widget()函数
        self.curve_widget.update_window_image.connect(lambda x: self.update_from_curve_widget(x))
        self.curve_widget.setWindowModality(QtCore.Qt.ApplicationModal)  # freeze window when widget is alive
        self.curve_widget.show()
        # 据控件返回的灰度图，调用b.hist_regulate(img, channel, hist)调节灰度
        # target = self.curve_widget.draw_curve()  # 从curve_widget里面获得新的曲线对应的灰度数组
        #channel = self.curve_widget.comboBox.currentText()
        # channel = self.curve_widget.update_image()
        # channel = 'RGB'  # 从curve_widget里面获得comboxBox.currentText()
        # self.tmp_img = b.hist_regulate(self.tmp_img, channel, target)
        # self.update_image()

    def crop_enter(self):
        crop = CropLabel(self.ui.imageLabel, self.x, self.y)
        crop.setFixedSize(self.ui.imageLabel.size())
        crop.finish.connect(lambda boundary: self.crop_exit(boundary))
        crop.show()

    def crop_exit(self, boundary):
        self.write_changes()
        self.tmp_img = self.tmp_img.crop(boundary)

        self.x = self.tmp_img.width
        self.y = self.tmp_img.height
        self.resize_ui()
        # self.update_image()

    def update_image(self):
        """
        更新图像显示
        Function for updating the image
        """
        # Way #1
        self.tmp_img.save('./test/tmp_img', 'BMP')
        pixmap = QPixmap('./test/tmp_img')  # This is rather slow.

        # Way #2
        # pixmap = QPixmap.fromImage(ImageQt(self.tmp_img))  # This won't work properly.

        # pixmap = ImageQt.toqpixmap(self.tmp_img)
        self.ui.imageLabel.setPixmap(pixmap.scaled(self.ui.imageLabel.size()))

        # update mode as well
        self.update_mode()

    def update_mode(self):
        # get image type and set disability accordingly
        if self.tmp_img.mode == 'L':
            self.ui.actionEqualization.setEnabled(True)
            self.ui.actionGrayscale.setEnabled(False)
            self.ui.actionBinary.setEnabled(True)
            self.ui.actionCurve.setEnabled(True)
        elif self.tmp_img.mode == '1':
            self.ui.actionEqualization.setEnabled(False)
            self.ui.actionGrayscale.setEnabled(False)
            self.ui.actionBinary.setEnabled(False)
            self.ui.actionCurve.setEnabled(False)
        else:
            self.ui.actionEqualization.setEnabled(True)
            self.ui.actionGrayscale.setEnabled(True)
            self.ui.actionBinary.setEnabled(True)
            self.ui.actionCurve.setEnabled(True)

    def update_from_curve_widget(self, channel):
        self.write_changes()
        self.tmp_img = b.hist_regulate(self.tmp_img_copy, channel, self.target)
        self.update_image()

    def write_changes(self):
        self.undo_stack.append(self.tmp_img)
        self.ui.actionUndo.setEnabled(True)
        self.ui.actionRedo.setEnabled(False)
        self.redo_stack.clear()
        self.unsaved = True
